import 'dart:async';
import 'dart:io';
import 'dart:typed_data';
import 'package:xsdk_flutter/xsdk/framework/bean/x_bean_device.dart';
import 'package:xsdk_flutter/xsdk/utils/json/x_utils_json.dart';
import 'package:xsdk_flutter/xsdk/utils/queue/x_bean_queue_message.dart';
import 'package:xsdk_flutter/xsdk/operation/x_mixin_release.dart';
import 'package:xsdk_flutter/xsdk/utils/socket/base/x_socket_connect_item.dart';
import 'package:xsdk_flutter/xsdk/utils/socket/support/x_socket_notify_callback.dart';
import 'package:xsdk_flutter/xsdk/utils/socket/x_utils_socket.dart';
import 'package:xsdk_flutter/xsdk/utils/x_utils_date.dart';
import 'package:xsdk_flutter/xsdk/utils/x_utils_log.dart';

/*
适用: 长连接,自动重连
 */
class XClientSocketLongConnectAPI with XMixinRelease{


  late String serverIP;
  late int serverPort;
  //是否自动发送自定义心跳包
  late bool isAutoSendCustomHeartBeatMsg;
  late bool isCustomSocketCommunication;



  XBaseDevice device;
  XSocketConnectItem? _socketConnectItem;

  XClientSocketLongConnectAPI(this.device){
    serverIP=device.ip??'';
    if (device.port <=1024){
      device.port=2222;
    }
    serverPort=device.port;
    isAutoSendCustomHeartBeatMsg=device.isAutoSendCustomHeartBeatMsg;
    isCustomSocketCommunication=device.isCustomSocketCommunication;

  }


  @override
  void xRelease() {
    //释放资源
    XLogUtils.printLog('---socket客户端xRelease');
    _isAutoReconnect=false;
    device.isConnected=false;

    try {
      _socketConnectItem?.xRelease();
      _timer?.cancel();
    }catch (error) {
      //
    }finally{
      _socketConnectItem=null;
      _timer = null;
    }

  }

  //1.启动客户端socket
  bool isConnecting=false;//是否正在连接中

  void connect(){

    if(!isConnecting && !device.isConnected){
      isConnecting=true;

      try {
        if(_socketConnectItem!=null){
          _socketConnectItem?.xRelease();
          _socketConnectItem=null;
        }
      }catch (error) {
        //
      }

      //
      Socket.connect(serverIP, serverPort,timeout: const Duration(seconds: 10)).then((Socket socket){
        XLogUtils.printLog('---socket客户端连接成功!');
        device.isConnected=true;
        isConnecting=false;
        XSocketDeviceNotifyCallback.instance.clientSocketDeviceConnectStatusCallback(
            true,
            serverIP,
            serverPort
        );
        _socketConnectItem=XSocketConnectItem(
            isServer:false,
            isCustomSocketCommunication:isCustomSocketCommunication,
            socket:socket,
            isReceiveMsg:device.customType=='IPPrinter'?false:device.isReceiveDeviceMsg,//适配旧的已设置过的网络打印机(包道)
            disconnectCallback:_handleSocketDisconnect
        );
      }).onError((error, stackTrace){
        isConnecting=false;
        XLogUtils.printLog('---XClientSocketAPI,connect(), onError= $error, stacktrace= $stackTrace');
        //处理服务端断连
        _handleSocketDisconnect(serverIP,serverPort);
      });
    }

  }


  //2.发送消息
  bool sendMessage(XBaseQueueMessage msg){
    try{
      if(_socketConnectItem!=null && _socketConnectItem!.socket!=null && device.isConnected){
        return _socketConnectItem!.sendMsg(msg);
      }
    }catch(e){
      //
    }
    return false;
  }


  ///////////////////////////////////////////////////////////////////////////

  Timer? _timer;
  bool _isAutoReconnect=true;

  //处理socket断连
  void _handleSocketDisconnect(String? ip,int? port){
    XLogUtils.printLog('---处理服务端断连,ip=$ip,port=$port');
    device.isConnected=false;
    XSocketDeviceNotifyCallback.instance.clientSocketDeviceConnectStatusCallback(false,ip,port);
    //
    _socketConnectItem?.xRelease();
    _socketConnectItem=null;

    //自动重连
    if(_isAutoReconnect){
      _timer ??= Timer(const Duration(seconds: 2),(){
        //
        XLogUtils.printLog('---自动重连: serverIP=$serverIP, serverPort=$serverPort');
        //
        connect();
        //
        _timer?.cancel();
        _timer = null;
      }
      );
    }


  }


  ///////////////////////////////////////////////////////////////////////////


  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
          other is XClientSocketLongConnectAPI &&
              runtimeType == other.runtimeType &&
              serverPort == other.serverPort &&
              serverIP == other.serverIP;

  @override
  int get hashCode => serverPort.hashCode ^ serverIP.hashCode;
}

/*
非自定义socket通讯,短连接,(如:网络socket打印机)
不自动重连,注意:利用10秒心跳自动删除
 */
class XClientSocketShortConnectAPI with XMixinRelease{

  late String serverIP;
  late int serverPort;
  late int createTime;

  Socket? socket;
  XBaseDevice device;

  /*
  是否自定义socket通讯
   */
  late bool isCustomSocketCommunication;

  XClientSocketShortConnectAPI(this.socket,this.device){
    serverIP=socket!.remoteAddress.address;
    serverPort=socket!.remotePort;
    createTime=XDateUtils.getCurrentTime_ms();
    isCustomSocketCommunication=device.isCustomSocketCommunication;

    XLogUtils.printLog('---成功创建的短连接:ip=$serverIP,serverPort=$serverPort');

    //状态
    socket?.done.onError((error, stackTrace){
      XLogUtils.printLog('---socket done报错= $error, stackTrace=$stackTrace');
      xRelease();
    }).whenComplete((){
      //实测:socket断开时结束回调
      XLogUtils.printLog('---socket done whenComplete');
      xRelease();
    });

    //接收消息(单向连接,不需要接收信息)
    // if(device.isReceiveDeviceMsg){
    //   _receiveMsg();
    // }

  }


  @override
  void xRelease() {
    //释放资源
    try {
      //socket?.close();//只关闭本端,不能发送数据
      socket?.destroy();//关闭双向,不在接收和发送数据
    }catch (error) {
      //
    }finally{
      socket=null;
    }

  }

  //2.1.发送消息
  bool sendMessage(XBaseQueueMessage msg){
    //实测断连发送也不会报错,因此无法使用trycatch拦截

    try{

      //实测:socket.destroy()后,如果不置空,则写入数据依然不会报错,正常通过,socket?.done.onError状态也不会报错
      if(socket!=null){
        if(msg.message_Uint8List!=null){
          socket!.add(msg.message_Uint8List!);
        }else if(msg.message!=null) {
          //实测write/writeln在断连发送也不会报错,因此无法使用trycatch拦截,因此要
          //socket.writeln(msg);//实测会分开发送内容后,再发送\n,导致接收消息时是分开2次接收,因此该用write

          /*
          flutter socket接收不按换行符标识接收,按每次发送内容接收
          XJsonUtils.obj2Json():适配map和string
          自动添加\n结尾,适配android原生等接收规则
           */
          String str=XJsonUtils.obj2Json(msg.message);
          //防止中间有\n
          str=str.replaceAll('\n', '');
          //
          socket!.write('$str\n');
        }
        // await socket.flush();//实测不要在write/writeln后使用socket.flush();会报错

        return true;
      }

    }catch(e){
      XLogUtils.printLog('---socket sendMessage报错:$e');
      xRelease();
    }

    return false;
  }


  //接收消息
  void _receiveMsg(){
    try{
      //注意:socket.listen()只能订阅一次,会替代旧的
      //socket?.asBroadcastStream().listen() 使用多个订阅
      socket?.listen((Uint8List data) {
        //utf8
        //String msg=XStringUtils.uint8ListToString_utf8(data);
        //
        //XLogUtils.printLog('---_receiveMsg收到socket消息=$msg');
        //返回对方(客户端或服务端)的地址和端口,而socket.address.address和socket.port是自己的地址和端口
        // XSocketDeviceNotifyCallback.instance.receiveSocketDeviceMsgCallback(
        //     isServerSendToClientMessage,
        //     ip,//socket.remoteAddress.address,
        //     port,//socket.remotePort,
        //     msg
        // );

      }).onError((e){
            XLogUtils.printLog('---接收socket信息报错, socket已断开: $e');
            xRelease();
          }
      );
    }catch(e){
      //
      XLogUtils.printLog('---接收socket信息报错: $e');
      xRelease();
    }


  }

  ///////////////////////////////////////////////////////////////////////////

  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
      other is XClientSocketShortConnectAPI &&
          runtimeType == other.runtimeType &&
          serverIP == other.serverIP &&
          serverPort == other.serverPort;

  @override
  int get hashCode => serverIP.hashCode ^ serverPort.hashCode;



}



